package server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.Semaphore;

import server.datamodel.CreepSmashGame;
import server.datamodel.Datamodel;
import server.datamodel.User;
import server.parser.LoginParser;
import server.parser.Parser;

public class ConnectionHandler implements Runnable {
	private Connection provider;
	private Socket socket;
	
	private DataOutputStream dataOutputStream;
	private DataInputStream dataInputStream;
	
	private Parser currentParser;
	private User currentUser;
	
	private boolean isRunning;
	private boolean connectionClosed = false;;
	private Semaphore lock;

	public ConnectionHandler(Connection provider, Socket socket, DataOutputStream dataOutputStream) {
		this.provider = provider;
		this.socket = socket;
		this.dataOutputStream = dataOutputStream;
		
		lock = new Semaphore(1);
		currentParser = new LoginParser(this);
		
		sendMessage("Welcome on creepsmash server");
	}
	
	@Override
	public void run() {
		isRunning = true;
		
		try {
			dataInputStream = new DataInputStream(socket.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			while (isRunning) {
				String s = dataInputStream.readUTF();

				if(currentParser != null) {
					currentParser.parseMessage(s);
				}
			}
		} catch (EOFException eof) {
			eof.printStackTrace();
		} catch (IOException e) {
			//can be forced, when disconnecting
			e.printStackTrace();
		}
		
		closeConnection();
	}
	
	private void closeConnection() {
		if(connectionClosed) {
			return;
		}
		
		disconnect();
		provider.disconnect(this);
		removeFromDatamodel();
		connectionClosed = true;
	}
	
	public void disconnect() {
		try {
			socket.close();
		} catch (IOException e) {
			//may be forced the disconnect
			e.printStackTrace();
		}
	}
	
	private void removeFromDatamodel() {
		//be sure, that this handler is removed from all games, thus no updates are sent. 
		for(CreepSmashGame g: getDatamodel().getGames()) {
			g.removeUsersInThisGame(this);
		}
				
		if(currentUser != null) {
			if(currentUser.getUserAssets() != null) {
				if(currentUser.getUserAssets().getTile() != null) {
					currentUser.getUserAssets().setTile(null);
				}
				
				currentUser.getUserAssets().getGame().removeUserAssets(currentUser.getUserAssets());
				currentUser.setUserAssets(null); 
			}
		}
	}
	
	public Socket getSocket() {
		return socket;
	}
	
	public void aquire() {
		try {
			lock.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void release() {
		lock.release();
	}
	
	public void sendMessage(String message) {
		aquire();
		
		sendAsynchronMessage(message);
		
		release();
	}
	
	public void sendAsynchronMessage(String message) {
		if(connectionClosed) {
			return;
		}
		
		try {
			dataOutputStream.writeUTF(message);
			System.out.println("send: " + message);
		} catch (IOException e) {
			e.printStackTrace();
			release();
			closeConnection();
		}
	}
	
	public void setCurrentParser(Parser currentParser) {
		this.currentParser = currentParser;
	}

	public Datamodel getDatamodel() {
		return provider.getDatamodel();
	}
	
	public Connection getProvider() {
		return provider;
	}
	
	public void setCurrentUser(User currentUser) {
		this.currentUser = currentUser;
	}
	
	public User getCurrentUser() {
		return currentUser;
	}
}